library ieee;
use ieee.std_logic_1164.all;
use IEEE.NUMERIC_STD.all;
use ieee.std_logic_unsigned.all;
use work.typeDefinitions.all;


entity coco is
  port
    (
      clk, nReset : in std_logic;

      --dcache 1 signals

      --what 1 is transmitting
      TxBusOp1   : in memBusOp;
      TxBusAddr1 : in std_logic_vector(31 downto 0);
      TxBusResp1 : in memBusResp;
      TxBusData1 : in std_logic_vector(63 downto 0);

      --what 1 is receiving
      RxBusOp1   : out memBusOp;
      RxBusAddr1 : out std_logic_vector(31 downto 0);
      RxBusResp1 : out memBusResp;
      RxBusData1 : out std_logic_vector(63 downto 0);

      override1 : out std_logic;

      --dcache 2 signals

      override2 : out std_logic;
      
      --what 2 is transmitting
      TxBusOp2   : in memBusOp;
      TxBusAddr2 : in std_logic_vector(31 downto 0);
      TxBusResp2 : in memBusResp;
      TxBusData2 : in std_logic_vector(63 downto 0);

      --what 2 is receiving
      RxBusOp2   : out memBusOp;
      RxBusAddr2 : out std_logic_vector(31 downto 0);
      RxBusResp2 : out memBusResp;
      RxBusData2 : out std_logic_vector(63 downto 0)
      );
end coco;

architecture coco_arch of coco is
  type state is (s_idle, s_left, s_right, jumpBall);
  type possession is (pos_left, pos_right);

  signal currState, nextState : state;
  signal prevAddr1, prevAddr2 : std_logic_vector(31 downto 0);
  signal possArrow, nextPoss  : possession;

begin  -- coco_arch


  -- bus state machine
  nstate : process(clk, nReset)
  begin
    if(nReset = '0') then
      currState <= s_idle;
      possArrow <= pos_left;
      prevAddr1 <= (others => '0');
      prevAddr2 <= (others => '0');
    elsif(rising_edge(clk)) then
      currState <= nextState;
      possArrow <= nextPoss;
      prevAddr1 <= TxBusAddr1;
      prevAddr2 <= TxBusAddr2;
    end if;
  end process;

  nsl : process (currState, prevAddr1, prevAddr2, possArrow, TxBusOp1, TxBusAddr1,
                 TxBusResp1, TxBusData1, TxBusOp2, TxBusAddr2, TxBusResp2, TxBusData2)
  begin  -- process nsl
    
    nextState <= currState;
    nextPoss  <= possArrow;

    case currState is
      when s_idle =>
        if((TxBusAddr1(31 downto 3) = TxBusAddr2(31 downto 3)) and
           (TxBusOp1 /= bus_txnop) and (TxBusOp2 /= bus_txnop)) then
          if((prevAddr1(31 downto 3) /= TxBusAddr1(31 downto 3)) and
             (prevAddr2(31 downto 3) /= TxBusAddr2(31 downto 3))) then
            --jumpBall
            if(possArrow = pos_left) then
              nextPoss  <= pos_right;
              nextState <= s_left;
            else
              nextPoss  <= pos_left;
              nextState <= s_right;
            end if;
          else
            --not a jump ball but our addresses now conflict.
            if(prevAddr1(31 downto 3) = TxBusAddr1(31 downto 3)) then
              --well left side was already doing stuff with this address
              nextState <= s_left;
            else
              nextState <= s_right;
            end if;
          end if;
        end if;
      when s_left =>
        if(TxBusAddr1(31 downto 3) /= TxBusAddr2(31 downto 3)) then
          nextState <= s_idle;
        end if;
      when s_right =>
        if(TxBusAddr1(31 downto 3) /= TxBusAddr2(31 downto 3)) then
          nextState <= s_idle;
        end if;
      when others =>
        nextState <= currState;
        nextPoss  <= possArrow;
    end case;

  end process nsl;


  outl : process (currState, nextState, TxBusOp1, TxBusAddr1,
                  TxBusResp1, TxBusData1, TxBusOp2, TxBusAddr2, TxBusResp2, TxBusData2)
  begin  -- process outl
          RxBusOp1   <= bus_txnop;
          RxBusAddr1 <= (others => '0');
          RxBusResp1 <= bus_rxnop;
          RxBusData1 <= (others => '0');

          RxBusOp2   <= bus_txnop;
          RxBusAddr2 <= (others => '0');
          RxBusResp2 <= bus_rxnop;
          RxBusData2 <= (others => '0');
          
          override1 <= '0';
          override2 <= '0';
          
    case currState is      
      when s_idle =>
        if(nextState = s_idle) then
          RxBusOp1   <= TxBusOp2;
          RxBusAddr1 <= TxBusAddr2;
          RxBusResp1 <= TxBusResp2;
          RxBusData1 <= TxBusData2;

          RxBusOp2   <= TxBusOp1;
          RxBusAddr2 <= TxBusAddr1;
          RxBusResp2 <= TxBusResp1;
          RxBusData2 <= TxBusData1;
        elsif(nextState = s_right) then
          RxBusOp1   <= TxBusOp2;
          RxBusAddr1 <= TxBusAddr2;
          RxBusResp1 <= TxBusResp2;
          RxBusData1 <= TxBusData2;
          override1 <= '1';
          RxBusOp2   <= bus_txnop;
          RxBusAddr2 <= (others => '0');
          RxBusResp2 <= TxBusResp1;
          RxBusData2 <= TxBusData1;
        elsif(nextState = s_left) then
          override2 <= '1';
          
          RxBusOp1   <= bus_txnop;
          RxBusAddr1 <= (others => '0');
          RxBusResp1 <= TxBusResp2;
          RxBusData1 <= TxBusData2;

          RxBusOp2   <= TxBusOp1;
          RxBusAddr2 <= TxBusAddr1;
          RxBusResp2 <= TxBusResp1;
          RxBusData2 <= TxBusData1;
        end if;

      when s_left =>
        RxBusOp1   <= bus_txnop;
        RxBusAddr1 <= (others => '0');
        RxBusResp1 <= TxBusResp2;
        RxBusData1 <= TxBusData2;
        override2 <= '1';
        RxBusOp2   <= TxBusOp1;
        RxBusAddr2 <= TxBusAddr1;
        RxBusResp2 <= TxBusResp1;
        RxBusData2 <= TxBusData1;
      when s_right =>
        RxBusOp1   <= TxBusOp2;
        RxBusAddr1 <= TxBusAddr2;
        RxBusResp1 <= TxBusResp2;
        RxBusData1 <= TxBusData2;
        override1 <= '1';
        RxBusOp2   <= bus_txnop;
        RxBusAddr2 <= (others => '0');
        RxBusResp2 <= TxBusResp1;
        RxBusData2 <= TxBusData1;
      when others =>
        RxBusOp1   <= TxBusOp2;
        RxBusAddr1 <= TxBusAddr2;
        RxBusResp1 <= TxBusResp2;
        RxBusData1 <= TxBusData2;

        RxBusOp2   <= TxBusOp1;
        RxBusAddr2 <= TxBusAddr1;
        RxBusResp2 <= TxBusResp1;
        RxBusData2 <= TxBusData1;
    end case;
    
  end process outl;
  
end coco_arch;

